//
//  sock_act.c
// 
//
//  Created by  AbbyCin on 10/28/14.
//  Copyright (C) Michael Kerrisk, 2014. All rights reserved.
//
//

#include "sock_act.h"
#include <strings.h> /* bzero */

/* bool en_disable: listen or bind */

static int sockact(const char *hostname, const char *service, int type, socklen_t *address_len,bool en_disable, int backlog)
{
    struct addrinfo *cur;
    struct addrinfo hints;
    struct addrinfo *res = NULL;   /* result */
    int sfd = 0;
    int opt =1;

    bzero(&hints,sizeof(struct addrinfo));
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    hints.ai_socktype = type;
    hints.ai_family = AF_UNSPEC;    /* IPv4 or IPv6 */

    getaddrinfo(hostname, service, &hints, &res);

    for(cur = res; cur != NULL; cur = cur->ai_next)
    {
        sfd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
        if(sfd == -1)
            continue;       /* try again */

        if(en_disable)
            if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
            {
                close(sfd);
                freeaddrinfo(res);
                return -1;
            }

        if(bind(sfd, cur->ai_addr, cur->ai_addrlen) == 0)
            break;              /* bind succeed */
        close(sfd);
    }

    if(cur != NULL && en_disable)
        if(listen(sfd,backlog) == -1)
        {
            freeaddrinfo(res);
            return -1;
        }

    if(cur !=NULL && address_len != NULL)
        *address_len = cur->ai_addrlen;

    freeaddrinfo(res);

    return (cur == NULL)? -1: sfd;
}

int Bind(const char *hostname, const char *service, int type, socklen_t *address_len)
{
    return sockact(hostname, service, type, address_len, false, 0);
}

int Listen(const char *hostname, const char *service, socklen_t *address_len, int backlog)
{
    return sockact(hostname, service, SOCK_STREAM, address_len, true, backlog);
}

int Connect(const char *hostname, const char *service, int type)
{
    struct addrinfo *cur;
    struct addrinfo hints;
    struct addrinfo *res = NULL;   /* result */
    int sfd = 0;

    bzero(&hints,sizeof(struct addrinfo));
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    hints.ai_socktype = type;
    hints.ai_family = AF_UNSPEC;    /* IPv4 or IPv6 */

    getaddrinfo(hostname, service, &hints, &res);

    for(cur = res; cur != NULL; cur = cur->ai_next)
    {
        sfd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
        if(sfd == -1)
            continue;       /* try again */
        if(connect(sfd, cur->ai_addr, cur->ai_addrlen) != -1)
            break;

        /* if connect failed, close socket and try again */
        close(sfd);
    }

    freeaddrinfo(res);

    return (cur == NULL)? -1: sfd;      /* connect failed return -1 */
}
